package MaquinaP.Estructura;

import java.util.ArrayList;
//pila para la estructura de la maquina P, donde guardar los variables
public class Pila {
	
	private ArrayList<Object> pila;
	private int numElem;// numero de elementos
	private TablaSimbolos var; //tabla de simbolos que apunta a la propia del interprete
	
	//constructora
	public Pila(TablaSimbolos v){
		pila = new ArrayList<Object>();
		numElem = 0;
		var = v;
	}
	//apila un elemento si es posible
	public boolean apila(Object o){
		if(pila.add(o)){
			numElem++;
			return true;
		}		
		return false;
	}
	// apila el valor del elemento posicionado en la direccion r
	public boolean apilaDir(int r){
		Object o = var.get(r).getValor();
		if(pila.add(o)){
			numElem++;
			return true;
		}		
		return false;
	}
	//desapila un elemento y guarda ese valor en la posicion de memoria i
	public boolean desapilaDir(int i){
	  if(!pilaVacia()){
		numElem--;
		var.get(i).setValor(pila.get(numElem));
		pila.remove(numElem);
		return true;
	  }
	  return false;
	}
	// desapila un elemento
	private boolean desapila(){
		if(!pilaVacia()){
			numElem--;
			pila.remove(numElem);
			return true;
		}
		return false;
	}
	// mira se la pila esta vacia
	public boolean pilaVacia(){
		return (numElem==0);
	}
	// metodo para pasar la pila a string
	public String toString(){
		return pila.toString();
	}
	
	
	//-----------------------FUNCIONES----------------------
	//funcion para aplicar la operacion out
	public void out(){
		Object o = pila.get(numElem-1);
		if (desapila()){
			System.out.println(o.toString());
		}
	}
/******************************************************************************************
	******Todas las funciones son muy parecidas asi que solo comentare la primera**********
	***************************************************************************************/
	//funcion para aplicar la operacion suma
	public void suma(){
		//obtenemos los dos valores mas arriba de la pila, ya que es una operacion binaria
		Object o1 = pila.get(numElem-1);
		Object o2 = pila.get(numElem-2);
		String s1;
		String s2;
		//vemos si los dos valores son de tipo int
		if ((o1 instanceof Integer)&&(o2 instanceof Integer)){
			s1 = o1.toString();
			s2 = o2.toString();
			//como son de tipo int, el resultado debe de ser un entero
			Integer d = Integer.valueOf(s1) + Integer.valueOf(s2);
			desapila();//desapilamos los dos elementos
			desapila();
			apila(d);//apilamos el resultado de la operacion
		}
		else{
			s1 = o1.toString();
			s2 = o2.toString();
			//como uno de los elementos por lo menos es real, el resultado es real
			Double d = Double.valueOf(s1) + Double.valueOf(s2);
			desapila();//desapilamos los dos elementos
			desapila();
			apila(d);//apilamos el resultado
		}	
		
	}
	//funcion para aplicar la operacion multiplicacion
	public void multiplicacion(){
		Object o1 = pila.get(numElem-1);
		Object o2 = pila.get(numElem-2);
		String s1;
		String s2;

		if ((o1 instanceof Integer)&&(o2 instanceof Integer)){
			s1 = o1.toString();
			s2 = o2.toString();
			Integer d = Integer.valueOf(s1) * Integer.valueOf(s2);
			desapila();
			desapila();
			apila(d);
		}
		else{
			s1 = o1.toString();
			s2 = o2.toString();
			Double d = Double.valueOf(s1) * Double.valueOf(s2);
			desapila();
			desapila();
			apila(d);
		}	
		
	}
	//funcion para aplicar la operacion resta
	public void resta(){
		Object o2 = pila.get(numElem-1);
		Object o1 = pila.get(numElem-2);
		String s1;
		String s2;

		if ((o1 instanceof Integer)&&(o2 instanceof Integer)){
			s1 = o1.toString();
			s2 = o2.toString();
			Integer d = Integer.valueOf(s1) - Integer.valueOf(s2);
			desapila();
			desapila();
			apila(d);
		}
		else{
			s1 = o1.toString();
			s2 = o2.toString();
			Double d = Double.valueOf(s1) - Double.valueOf(s2);
			desapila();
			desapila();
			apila(d);
		}	
		
	}
	//funcion para aplicar la operacion division
	public boolean division(){
		Object o2 = pila.get(numElem-1);
		Object o1 = pila.get(numElem-2);
		String s1;
		String s2;

		if ((o1 instanceof Integer)&&(o2 instanceof Integer)){
			s1 = o1.toString();
			s2 = o2.toString();
			//comprobamos que el divisor no sea 0 ya que es un fallo matematico y aborta la ejecucion
			if(Integer.valueOf(s2) == 0){
				System.out.println("Error : Division por 0");
				return false;
			}
			Integer d = Integer.valueOf(s1) / Integer.valueOf(s2);
			desapila();
			desapila();
			apila(d);
			
		}
		else{
			s1 = o1.toString();
			s2 = o2.toString();
			if(Double.valueOf(s2) == 0){
				System.out.println("Error : Division por 0");
				return false;
			}
			Double d = Double.valueOf(s1) / Double.valueOf(s2);
			desapila();
			desapila();
			apila(d);
		}	
		return true;
		
	}
	//funcion para aplicar la operacion menor, el resultado es siempre entero
	public void menor(){
		Object o2 = pila.get(numElem-1);
		Object o1 = pila.get(numElem-2);
		String s1;
		String s2;

		if ((o1 instanceof Integer)&&(o2 instanceof Integer)){
			s1 = o1.toString();
			s2 = o2.toString();
			int sol=0;
			if(Integer.valueOf(s1) < Integer.valueOf(s2))
				sol = 1;
			desapila();
			desapila();
			apila(sol);
		}
		else{
			s1 = o1.toString();
			s2 = o2.toString();
			int sol = 0;
			if(Double.valueOf(s1) < Double.valueOf(s2))
				sol = 1;
			desapila();
			desapila();
			apila(sol);
		}	
	}
	//funcion para aplicar la operacion mayor, el resultado es siempre entero
	public void mayor(){
		Object o2 = pila.get(numElem-1);
		Object o1 = pila.get(numElem-2);
		String s1;
		String s2;

		if ((o1 instanceof Integer)&&(o2 instanceof Integer)){
			s1 = o1.toString();
			s2 = o2.toString();
			int sol=0;
			if(Integer.valueOf(s1) > Integer.valueOf(s2))
				sol = 1;
			desapila();
			desapila();
			apila(sol);
		}
		else{
			s1 = o1.toString();
			s2 = o2.toString();
			int sol = 0;
			if(Double.valueOf(s1) > Double.valueOf(s2))
				sol = 1;
			desapila();
			desapila();
			apila(sol);
		}	
	}
	//funcion para aplicar la operacion menor o igual, el resultado es siempre entero
	public void menorIgual(){
		Object o2 = pila.get(numElem-1);
		Object o1 = pila.get(numElem-2);
		String s1;
		String s2;

		if ((o1 instanceof Integer)&&(o2 instanceof Integer)){
			s1 = o1.toString();
			s2 = o2.toString();
			int sol=0;
			if(Integer.valueOf(s1) <= Integer.valueOf(s2))
				sol = 1;
			desapila();
			desapila();
			apila(sol);
		}
		else{
			s1 = o1.toString();
			s2 = o2.toString();
			int sol = 0;
			if(Double.valueOf(s1) <= Double.valueOf(s2))
				sol = 1;
			desapila();
			desapila();
			apila(sol);
		}	
	}
	//funcion para aplicar la operacion mayor o igual, el resultado es siempre entero
	public void mayorIgual(){
		Object o2 = pila.get(numElem-1);
		Object o1 = pila.get(numElem-2);
		String s1;
		String s2;

		if ((o1 instanceof Integer)&&(o2 instanceof Integer)){
			s1 = o1.toString();
			s2 = o2.toString();
			int sol=0;
			if(Integer.valueOf(s1) >= Integer.valueOf(s2))
				sol = 1;
			desapila();
			desapila();
			apila(sol);
		}
		else{
			s1 = o1.toString();
			s2 = o2.toString();
			int sol = 0;
			if(Double.valueOf(s1) >= Double.valueOf(s2))
				sol = 1;
			desapila();
			desapila();
			apila(sol);
		}	
		
	}
	//funcion para aplicar la operacion igual, el resultado es siempre entero
	public void igual(){
		Object o2 = pila.get(numElem-1);
		Object o1 = pila.get(numElem-2);
		String s1;
		String s2;

		if ((o1 instanceof Integer)&&(o2 instanceof Integer)){
			s1 = o1.toString();
			s2 = o2.toString();
			int sol=0;
			if(Integer.valueOf(s1).equals(Integer.valueOf(s2)))
				sol = 1;
			desapila();
			desapila();
			apila(sol);
		}
		else{
			s1 = o1.toString();
			s2 = o2.toString();
			int sol = 0;
			if(Double.valueOf(s1).equals(Double.valueOf(s2)))
				sol = 1;
			desapila();
			desapila();
			apila(sol);
		}	
		
	}
	//funcion para aplicar la operacion distinto, el resultado es siempre entero
	public void distinto(){
		Object o2 = pila.get(numElem-1);
		Object o1 = pila.get(numElem-2);
		String s1;
		String s2;

		if ((o1 instanceof Integer)&&(o2 instanceof Integer)){
			s1 = o1.toString();
			s2 = o2.toString();
			int sol=0;
			if(!Integer.valueOf(s1).equals(Integer.valueOf(s2)))
				sol = 1;
			desapila();
			desapila();
			apila(sol);
		}
		else{
			s1 = o1.toString();
			s2 = o2.toString();
			int sol = 0;
			if(!Double.valueOf(s1).equals(Double.valueOf(s2)))
				sol = 1;
			desapila();
			desapila();
			apila(sol);
		}	
		
	}
	//funcion para aplicar la operacion modulo
	public boolean modulo(){
		Object o2 = pila.get(numElem-1);
		Object o1 = pila.get(numElem-2);
		String s1;
		String s2;

		if ((o1 instanceof Integer)&&(o2 instanceof Integer)){
			s1 = o1.toString();
			s2 = o2.toString();
			//comprobamos que el segundo elemento sea distinto de 0, sino salta un error que aborta la ejecucion
			if(Integer.valueOf(s2) <= 0){
				System.out.println("Error : Division por 0");
				return false;
			}
			Integer d = Integer.valueOf(s1) % Integer.valueOf(s2);
			desapila();
			desapila();
			apila(d);
			
		}
		else{
			s1 = o1.toString();
			s2 = o2.toString();
			if(Double.valueOf(s2) <=   0){
				System.out.println("Error : Division por 0");
				return false;
			}
			Double d = Double.valueOf(s1) % Double.valueOf(s2);
			desapila();
			desapila();
			apila(d);
		}	
		return true;
	}
	//funcion para aplicar la operacion or, el resultado es siempre entero
	public void or(){
		Object o2 = pila.get(numElem-1);
		Object o1 = pila.get(numElem-2);
		String s1;
		String s2;
		int sol=1;
		if ((o1 instanceof Integer)&&(o2 instanceof Integer)){
			s1 = o1.toString();
			s2 = o2.toString();		
			if((Integer.valueOf(s1).equals(0))&&(Integer.valueOf(s2).equals(0)))
				sol = 0;
			desapila();
			desapila();
			apila(sol);
		}
		else{
			s1 = o1.toString();
			s2 = o2.toString();
			if((Double.valueOf(s1).equals(0))&&(Double.valueOf(s2).equals(0)))
				sol = 0;
			desapila();
			desapila();
			apila(sol);
		}	
	}
	//funcion para aplicar la operacion and, el resultado es siempre entero
	public void and(){
		Object o2 = pila.get(numElem-1);
		Object o1 = pila.get(numElem-2);
		String s1;
		String s2;
		int sol=1;
		if ((o1 instanceof Integer)&&(o2 instanceof Integer)){
			s1 = o1.toString();
			s2 = o2.toString();		
			if((Integer.valueOf(s1).equals(0))||(Integer.valueOf(s2).equals(0)))
				sol = 0;
			desapila();
			desapila();
			apila(sol);
		}
		else{
			s1 = o1.toString();
			s2 = o2.toString();
			if((Double.valueOf(s1).equals(0))||(Double.valueOf(s2).equals(0)))
				sol = 0;
			desapila();
			desapila();
			apila(sol);
		}	
	}
	//funcion para aplicar la operacion negacion, el resultado es siempre entero
	public void negacion(){
		Object o = pila.get(numElem-1);//solo cogemos un elemento
		int n = 0;
		if (o instanceof Integer){
			String s = o.toString();
			Integer num = Integer.valueOf(s);
			if(num.equals(0))
				n = 1;
			desapila();
			apila(n);
		}
		else if (o instanceof Double){
			String s = o.toString();
			Double num = Double.valueOf(s);
			if(num.equals(0))
				n = 1;
			desapila();
			apila(n);
		}
	}
	//funcion para aplicar la operacion menos
	public void menos(){
		Object o = pila.get(numElem-1);//solo cogemos un elemento
		if (o instanceof Integer){
			String s = o.toString();
			Integer num = -Integer.valueOf(s);
			desapila();
			apila(num);
		}
		else if (o instanceof Double){
			String s = o.toString();
			Double num = 0-Double.valueOf(s);
			desapila();
			apila(num);
		}
	}
	//funcion para aplicar la operacion casting int
	public void castInt(){
		Object o = pila.get(numElem-1);
		if (o instanceof Double){
			String s = o.toString();
			int index = s.indexOf('.');
			s = s.substring(0, index);
			Integer num = Integer.valueOf(s);
			desapila();
			apila(num);
		}
	}
	//funcion para aplicar la operacion casting real
	public void castReal(){
		Object o = pila.get(numElem-1);
		if (o instanceof Integer){
			String s = o.toString();
			Double num = Double.valueOf(s);
			desapila();
			apila(num);
		}
	}
	
	
}
